// ##########################################################################
// #                                                                        #
// #                              CLOUDCOMPARE                              #
// #                                                                        #
// #  This program is free software; you can redistribute it and/or modify  #
// #  it under the terms of the GNU General Public License as published by  #
// #  the Free Software Foundation; version 2 or later of the License.      #
// #                                                                        #
// #  This program is distributed in the hope that it will be useful,       #
// #  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
// #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          #
// #  GNU General Public License for more details.                          #
// #                                                                        #
// #          COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI)             #
// #                                                                        #
// ##########################################################################

#ifndef CC_CLIP_BOX_HEADER
#define CC_CLIP_BOX_HEADER

// Local
#include "ccBBox.h"
#include "ccGenericPointCloud.h"
#include "ccHObject.h"
#include "ccInteractor.h"

// Qt
#include <QObject>

class ccClipBoxPart;

//! Clipping box
class QCC_DB_LIB_API ccClipBox : public QObject
    , public ccHObject
    , public ccInteractor
{
	Q_OBJECT

  public:
	//! Default constructor
	/** \param name entity name (optional)
	    \param uniqueID unique ID (handle with care)
	**/
	ccClipBox(QString name = QString("Clipping box"), unsigned uniqueID = ccUniqueIDGenerator::InvalidUniqueID);

	//! Destructor
	~ccClipBox() override;

	//! Adds an associated entity
	/** Warning: resets the current clipping box
	 **/
	bool addAssociatedEntity(ccHObject* associatedEntity);

	//! Releases all associated entities
	/** Warning: resets the current clipping box
	 **/
	void releaseAssociatedEntities();

	// inherited from ccHObject
	ccBBox getOwnBB(bool withGLFeatures = false) override;

	// inherited from ccInteractor
	// bool move2D(int x, int y, int dx, int dy, int screenWidth, int screenHeight) override;
	bool move3D(const CCVector3d& u) override;

	//! Sets last clicked point (on screen)
	void setClickedPoint(int x, int y, int screenWidth, int screenHeight, const ccGLMatrixd& viewMatrix);

	//! Components
	enum Components
	{
		NONE = 0,
		X_MINUS_ARROW,
		X_PLUS_ARROW,
		Y_MINUS_ARROW,
		Y_PLUS_ARROW,
		Z_MINUS_ARROW,
		Z_PLUS_ARROW,
		CROSS,
		X_MINUS_TORUS,
		Y_MINUS_TORUS,
		Z_MINUS_TORUS,
		X_PLUS_TORUS,
		Y_PLUS_TORUS,
		Z_PLUS_TORUS
	};

	//! Sets currently active component
	/** \param id component ID (see Components)
	 **/
	void setActiveComponent(Components id);

	// inherited from ccHObject
	inline CC_CLASS_ENUM getClassID() const override
	{
		return CC_TYPES::CLIPPING_BOX;
	}

	//! Returns the box extents
	inline const ccBBox& getBox() const
	{
		return m_box;
	}

	//! Whether to show the box or not
	inline void showBox(bool state)
	{
		m_showBox = state;
	}

	//! Sets the box extents
	void setBox(const ccBBox& box);

	//! Shifts the current box
	void shift(const CCVector3& v);

	//! Flags the points of a given cloud depending on whether they are inside or outside of this clipping box
	/** \param cloud point cloud
	    \param visTable visibility flags
	    \param shrink Whether the box is shrinking (faster) or not
	**/
	void flagPointsInside(ccGenericPointCloud*                      cloud,
	                      ccGenericPointCloud::VisibilityTableType* visTable,
	                      bool                                      shrink = false) const;

	//! Resets box
	void reset();

	//! Manually sets the box parameters
	void set(const ccBBox& extents, const ccGLMatrix& transformation);

	//! Returns the box parameters
	void get(ccBBox& extents, ccGLMatrix& transformation);

	//! Associated entity container
	inline const ccHObject& getContainer() const
	{
		return m_entityContainer;
	}

  Q_SIGNALS:

	//! Signal sent each time the box is modified
	void boxModified(const ccBBox* box);

  protected: // methods
	//! Updates the associated entity clipping planes
	void update();

	// inherited from ccHObject
	void drawMeOnly(CC_DRAW_CONTEXT& context) override;

	//! Computes arrows display scale
	PointCoordinateType computeArrowsScale() const;

  protected: // members
	//! Associated entities container
	ccHObject m_entityContainer;

	//! Clipping box
	ccBBox m_box;

	//! Show box
	bool m_showBox;

	//! Active component
	Components m_activeComponent;

	//! Last "orientation" vector (corresponding to last clicked point)
	CCVector3d m_lastOrientation;

	//! View matrix
	ccGLMatrixd m_viewMatrix;

	//! Sub-parts (proxies)
	QMap<int, ccClipBoxPart*> m_parts;
};

//! Part of the clipping box (for picking)
class QCC_DB_LIB_API ccClipBoxPart : public ccHObject
{
  public:
	//! Default constructor
	/** \param color picking color
	    \param partID part ID
	**/
	ccClipBoxPart(ccClipBox* clipBox, ccClipBox::Components partID)
	    : m_clipBox(clipBox)
	    , m_partID(partID)
	{
	}

	// inherited from ccHObject
	inline CC_CLASS_ENUM getClassID() const override
	{
		return CC_TYPES::CLIPPING_BOX_PART;
	}

	//! Returns the corresponding part ID
	inline ccClipBox::Components partID() const
	{
		return m_partID;
	}

	//! Returns the associated clipping box
	ccClipBox* clipBox()
	{
		return m_clipBox;
	}

  protected:
	//! Associated clipping box
	ccClipBox* m_clipBox;
	//! Part number
	ccClipBox::Components m_partID;
};

#endif // CC_CLIP_BOX_HEADER
